/////////////////////////////////////////////////////////////////////
//
//  MOE Framework (Meta Object Encapsulation Framework)
//
//  Original Author: Wiksy Wang, 2011
//
/////////////////////////////////////////////////////////////////////

#ifndef MOE_NAME_NAMESPACE_H
#define MOE_NAME_NAMESPACE_H

#include <map>
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
#include <boost/unordered_map.hpp>

#include "NamePath.h"
#include "NamePathRef.h"
#include "../ObjectTypeManager.h"

namespace MOE
{

namespace Name
{

class IObjectToNameQuery;

// Namespace contains a list of named objects.
// The named objects can be operated upon with the exposed visitor function.
// The namespace isn't directly responsible for memory management of all child objects,
// however, it's responsible for all child namespaces.
// If a parent namespace is destroyed, then all child namespaces will also be destroyed.
class Namespace : boost::noncopyable
{
public:
	static Namespace& GetGlobalNamespace();
	static const RawByteVector& GetObjectName(void* object);

	static boost::shared_ptr<IObjectToNameQuery> GetObjectToNameQuery() { return gObjectToName; }
	static void SetObjectToNameQuery(boost::shared_ptr<IObjectToNameQuery> query) { gObjectToName = query; }

private:
	static boost::shared_ptr<IObjectToNameQuery> gObjectToName;

	struct ChildEntry
	{
		explicit ChildEntry(ClassIDType type = 0, void* ptr = NULL)
			: _type(type)
			, _pointer(ptr)
		{
		}

		explicit ChildEntry(Namespace* child)
			: _type(0)
			, _pointer(child)
		{
		}

		bool IsNamespace() const { return _type == InvalidClassID; }

		ClassIDType _type;
		void* _pointer;
	};

	typedef std::map<RawByteVector, ChildEntry> ChildMap;

public:
	Namespace* GetParent() { return _parent; }
	const Namespace* GetParent() const { return _parent; }

	const RawByteVector& GetName() const { return _name; }

	template <class T>
	T* GetObject(const NamePath& namePath) const
	{
		return static_cast<T*>(GetObject(namePath, ObjectTypeManager::GetClassID<T>()));
	}

	template <class T>
	void SetObject(const NamePath& namePath, T* object)
	{
		SetObject(namePath, object, ObjectTypeManager::GetClassID<T>());
	}

	Address GetObject(const NamePath& namePath, ClassIDType typeID) const;
	bool SetObject(const NamePath& namePath, void* object, ClassIDType typeID);
	bool RemoveObject(const NamePath& namePath);

	Namespace* GetChildNamespace(const NamePath& namePath);
	const Namespace* GetChildNamespace(const NamePath& namePath) const;
	Namespace* CreateChildNamespace(const NamePath& namePath);
	bool RemoveChildNamespace(const NamePath& namePath);

	void RemoveAll();

	// Visitor
	template <class Visitor>
	void Visit(Visitor& v) const
	{
		for (ChildMap::const_iterator iter = _managedObjects.begin(), iterEnd = _managedObjects.end(); iter != iterEnd; ++iter)
		{
			if (iter->second._type != InvalidClassID)
			{
				v.OnObject(iter->first, iter->second._type, iter->second._pointer);
			}
			else
			{
				v.OnNamespace(iter->first, static_cast<Namespace*>(iter->second._pointer));
			}
		}
	}

	// Construct the full path. Will be slow.
	template <class CharType>
	RawByteVector GetFullPath(const CharType delimiter = CharType('.')) const
	{
		return GetFullPathImpl(static_cast<unsigned int>(delimiter), sizeof(CharType));
	}

	// Construct the full path. Will be slow.
	RawByteVector GetChildFullPath(const NamePath& namePath) const
	{
		return GetChildFullPathImpl(namePath);
	}

	RawByteVector GetRelativePath(const NamePath& fullPath) const;

private:
	Namespace(Namespace* parent = NULL, const RawByteVector& name = RawByteVector::gEmptyStr);
	~Namespace();

	RawByteVector GetFullPathImpl(const unsigned int delimiter, const size_t charTypeSize) const;
	RawByteVector GetChildFullPathImpl(const NamePathRef& namePath) const;

	// TODO: a pretty bad way to use NamePathRef. Or having NamePathRef itself is a pretty bad thing.
	Address GetObjectImpl(const NamePathRef& namePath, ClassIDType typeID) const;
	bool SetObjectImpl(const NamePathRef& namePath, void* object, ClassIDType typeID);
	bool RemoveObjectImpl(const NamePathRef& namePath);

	Namespace* GetChildNamespaceImpl(const NamePathRef& namePath);
	const Namespace* GetChildNamespaceImpl(const NamePathRef& namePath) const;
	Namespace* CreateChildNamespaceImpl(const NamePathRef& namePath);
	bool RemoveChildNamespaceImpl(const NamePathRef& namePath);

	Namespace* _parent;
	RawByteVector _name;
	ChildMap _managedObjects;
};

}

}

#endif
